﻿/*文件注释
 * ===============================================================================
 * 说明：
 * 作者：曾永佑
 * 版本：
 * 创建日期：2013-02-20
 * 完成日期： 
 * 修改纪录：  
 * ===============================================================================
 */  
        
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace SRF.Data.Mapping
{
    /// <summary>
    /// 数据映射
    /// </summary>
    /// <typeparam name="TResult"></typeparam>
    public class DataMapper<TResult> : IDataMapper<TResult>
    {
        public DataMapper()
        {
        }
        /// <summary>
        /// 数据映射
        /// </summary>
        /// <param name="row"></param>
        /// <returns></returns>
        public TResult MapRow(System.Data.IDataReader row)
        {
            if (row.Read())
            {
                TResult obj;
                if (typeof(TResult).IsSealed)
                {
                    var r = ConvertValue(row[0], typeof(TResult));
                    obj = (TResult)r;
                }
                else
                {
                    obj = Activator.CreateInstance<TResult>();
                    PropertyInfo[] propertys = typeof(TResult).GetProperties();
                    for (int i = 0; i < row.FieldCount; i++)
                    {
                        var pi = propertys.SingleOrDefault(a => a.Name.ToLower() == row.GetName(i).ToLower());
                        if (pi != null)
                        {
                            SetValue(obj, pi, row[i]);
                        }
                    }
                }
                return obj;
            }
            else
                return default(TResult);
        }
        public IList<TResult> MapRows(System.Data.IDataReader reader)
        {
            IList<TResult> list = new List<TResult>();
            TResult item;
            while ((item = MapRow(reader)) != null)
            {
                list.Add(item);
            }
            return list;

        }

        #region

        /// <summary>
        /// 给属性赋值
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="property"></param>
        /// <param name="value"></param>
        private void SetValue(TResult obj, PropertyInfo property, object value)
        {
            var convertedValue = ConvertValue(value, property.PropertyType);
            property.SetValue(obj, convertedValue, new object[0]);
        }

        /// <summary>
        /// Converts the database value <paramref name="value"/> to <paramref name="conversionType"/>.
        /// </summary>
        protected static object ConvertValue(object value, Type conversionType)
        {
            if (IsNullableType(conversionType))
                return ConvertNullableValue(value, conversionType);
            else
                return ConvertNonNullableValue(value, conversionType);
        }

        private static bool IsNullableType(Type t)
        {
            return t.IsGenericType &&
                   t.GetGenericTypeDefinition() == typeof(Nullable<>);
        }

        /// <summary>
        /// Converts the database value <paramref name="value"/> to <paramref name="conversionType"/>,
        /// where <paramref name="conversionType"/> is a nullable value.
        /// </summary>
        /// <param name="value">Value from the database.</param>
        /// <param name="conversionType">Type to convert to.</param>
        /// <returns>The converted value.</returns>
        protected static object ConvertNullableValue(object value, Type conversionType)
        {
            if (value != DBNull.Value)
            {
                var converter = new System.ComponentModel.NullableConverter(conversionType);
                return converter.ConvertFrom(value);
            }
            return null;
        }

        /// <summary>
        /// Converts the database value <paramref name="value"/> to <paramref name="conversionType"/>.
        /// Will throw an exception if <paramref name="conversionType"/> is a nullable value.
        /// </summary>
        /// <param name="value">Value from the database.</param>
        /// <param name="conversionType">Type to convert to.</param>
        /// <returns>The converted value.</returns>
        protected static object ConvertNonNullableValue(object value, Type conversionType)
        {
            object convertedValue = null;

            if (value != DBNull.Value)
            {
                if (conversionType.IsEnum) //枚举型转换
                {
                    if (value is String)
                        convertedValue = Enum.Parse(conversionType, value.ToString(), true);
                    else
                        convertedValue = Enum.ToObject(conversionType, (int)value);
                }
                else
                {
                    if (value is IConvertible)
                    {
                        convertedValue = Convert.ChangeType(value, conversionType);
                    }
                    else
                        convertedValue = Convert.ToString(value);
                }
            }

            return convertedValue;
        }

        #endregion
    }
}